package dproxy.server;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;

import dproxy.server.common.exceptions.ServerConfigurationException;
import dproxy.server.common.xml.to.IntermediareConfig;
import dproxy.server.common.xml.to.ProtocolConfig;
import dproxy.server.concurrent.ThreadManager;
import dproxy.server.net.access.Intermediare;
import dproxy.server.net.protocol.Protocol;

/**
 * This server loads the application configuration and manages the intermediares
 * instances. To each dproxy-config (from jar or plain XML file) a Server instance
 * is created.
 */
public class Server {

    /** Server configuration (contents of XML file). */
    private ServerConfig config;

    /** Intermediares instances (defined on XML configuration). */
    private Collection<Intermediare> intermediares;

    /**
     * Constructor. Loads configuration and instantiates intermediares.
     * @param configFileStream XML deployment descriptor
     * @param classLoader Class loader where Protocol classes will be loaded
     */
    public Server(InputStream configFileStream, ClassLoader classLoader) {
        intermediares = new ArrayList<Intermediare>();
        
        // Parse XML configuration:
        config = new ServerConfig(configFileStream);
        try {
            // Instantiate Intermediares for each xml entry:
            for (IntermediareConfig ic : config.getIntermediareConfigs()) {
                ProtocolConfig protocolConfig = config.getProtocolConfigs()
                        .get(ic.getProtocolName());
                if (config != null) {
                    Class protocolClassDef = classLoader
                            .loadClass(protocolConfig.getProtocolClass());
                    Object loadedClass = protocolClassDef.newInstance();
                    if (loadedClass instanceof Protocol) {
                        // Set up protocol instance
                        Protocol protocol = (Protocol) loadedClass;
                        protocol.setDescription(protocolConfig.getDescription());
                        protocol.setInitParams(protocolConfig.getInitParams());
                        ic.setProtocol(protocol);
                        // Initiate protocol
                        protocol.init();
                    } else {
                        // Protocol does not implements Protocol interface
                        throw new ServerConfigurationException("Class "
                                + protocolConfig.getProtocolClass()
                                + " must be an "
                                + "instance of " + Protocol.class.getName());
                    }
                    Intermediare intermediare = new Intermediare(ic);
                    intermediares.add(intermediare);
                } else {
                    // <protocol /> with this name was not defined
                    throw new ServerConfigurationException(
                            "Intermediare references invalid protocol : ["
                                    + ic.getProtocolName() + "]");
                }
            }
        } catch (ClassNotFoundException e) {
            throw new ServerConfigurationException(e);
        } catch (InstantiationException e) {
            throw new ServerConfigurationException(e);
        } catch (IllegalAccessException e) {
            throw new ServerConfigurationException(e);
        }
    }

    /**
     * Starts all intermediares.
     */
    public void start() {
        for (Intermediare intermediare : intermediares) {
            intermediare.start();
        }
    }

    /**
     * Stops all intermediares.
     */
    public void stop() {
        for (Intermediare intermediare : intermediares) {
            intermediare.stop();
        }
        ThreadManager.getInstance().shutdown();
    }
}
